package model.editormodel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import locsi.UserSettings;
import model.LogicType;

/**
 * <b>In Verbindung stehende Klassen:</b> <code>LogicEditorElement</code><br>
 * <b>Funktion:</b> Repraesentiert eine Schaltung <br>
 * <br>
 * Stellt eine Schaltung mit den daraufliegenden Bauteilen, die als Map
 * hinterlegt sind, dar. Hat zusaetzlich Informationen ueber den Namen, die Zahl
 * der unverbundenen Ein- und Ausgaenge und ob es sich bei der Schaltung um ein
 * Schaltnetz oder Schaltwerk handelt.
 * 
 * @author Team Locsi
 * 
 */
public class CircuitModel {

	private String name;
	private Map<String, LogicEditorElement> elementsOnEditor;
	private int counter;
	private boolean isLogicCircuit;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>CircuitModel</code>-Objekt.
	 */
	protected CircuitModel() {
		this.name = "main";
		this.counter = 0;
		this.elementsOnEditor = new HashMap<String, LogicEditorElement>();
	}

	/**
	 * Diese Methode aktualisiert die <code>isLogicCircuit</code> Variable.
	 * Diese Methode wird benoetigt, wenn ein selbstdefiniertes Bauteil erzeugt
	 * wird und diesem ein CircuitModel uebergeben wird, welches ja dann nicht
	 * mehr veraenderbar ist und somit ist diese Information ab diesem Zeitpunkt
	 * gueltig.
	 */
	protected void setLogicCircuitType() {
		this.isLogicCircuit = this.isLogicCircuit();
	}

	/**
	 * Diese Methode ermoeglicht dem CircuitModel sich selbst Elemente, ohne
	 * Pruefung auf bereits vorhandenenen Namen, hinzuzufuegen. Diese Methode
	 * ist explizit fuer Parser gedacht, die bereits validierte Formate einer
	 * Schaltung einlesen.
	 * 
	 * @param type
	 *            <code>LogicType</code> - Typ des Elements
	 * @param distinctName
	 *            <code>String</code> eindeutiger Name des Elements
	 * @param xCoord
	 *            <code>double</code> - x-Koordinate des Bauteils
	 * @param yCoord
	 *            <code>double</code> - y-Koordinate des Bauteils
	 * @param cModel
	 *            <code>CircuitModel</code> - Aufbau eines selbstdefinierten
	 *            Bauteils
	 */
	protected void addLogicElement(LogicType type, String distinctName,
			double xCoord, double yCoord, CircuitModel cModel) {

		// Die Uebergabeparameter LogicType und distinctName duerfen nicht null
		// sein
		if (type == null || distinctName == null) {
			throw new IllegalArgumentException(
					"uebergabeparameter darf nicht null sein");
		} else {

			// ein selbstdefiniertes Bauteil muss gesondert behandelt werden
			if (type == LogicType.SELFDEFINED) {
				elementsOnEditor.put(distinctName,
						new LogicSelfdefinedEditorElement(distinctName, xCoord,
								yCoord, cModel));
			} else {
				elementsOnEditor.put(distinctName, new LogicEditorElement(type,
						distinctName, xCoord, yCoord));
			}
			this.increaseIdCounter();
		}
	}

	/**
	 * Diese Methode fuegt zur bestehenden Schaltung eine Verbindung hinzu.
	 * Dabei ist zu beachten, dass die Elemente zuvor angelegt werden muessen,
	 * denn ansonsten kann die Verbindung nicht angelegt werden.
	 * 
	 * @param source
	 *            <code>String</code> - Name der Quelle
	 * @param sourcePort
	 *            <code>String</code> - Name des Quellports
	 * @param target
	 *            <code>String</code> - Name des Ziels
	 * @param targetPort
	 *            <code>String</code> - Name des Zielports
	 * @return <code>boolean</code> - <code>true</code> wenn die Vebindung
	 *         erzeugt werden konnte, ansonsten <code>false</code>
	 */
	protected boolean setConnection(String source, String sourcePort,
			String target, String targetPort) {

		// Pruefung ob die benoetigten Elemente bereits vorhanden sind
		if (this.elementsOnEditor.containsKey(source)
				& this.elementsOnEditor.containsKey(target)) {
			LogicEditorElement sourceE = this.elementsOnEditor.get(source);
			LogicEditorElement targetE = this.elementsOnEditor.get(target);

			// Pruefung ob die benoetigten Ports vorhanden sind
			if (sourceE.getOutputs().containsKey(sourcePort)
					& targetE.getInputs().containsKey(targetPort)) {
				Output sourceO = sourceE.getOutputs().get(sourcePort);
				Input targetI = targetE.getInputs().get(targetPort);

				// pruefen ob der Eingang nicht bereits schon belegt ist
				if (targetI.getSourceOutput() == null) {
					targetI.setSourceOutput(sourceO);
				} else {
					return false;
				}

				// keine Pruefung notwendig, da ein Ausgang mit mehreren
				// Eingaengen
				// verbunden sein kann
				sourceO.setTargetInput(targetI);
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	/**
	 * Gibt alle <code>LogicEditorElement</code>-Objekte einer Schaltung
	 * zurueck.
	 * 
	 * @return <code>Map</code> - alle <code>LogicEditorElement</code>-Objekte
	 *         einer Schaltung
	 */
	protected Map<String, LogicEditorElement> getElementsOnEditor() {
		return this.elementsOnEditor;
	}

	/**
	 * Gibt alle <code>LogicEditorElement</code>-Objekte einer Schaltung zurueck
	 * 
	 * @return <code>List</code> - alle <code>LogicEditorElement</code>-Objekte
	 *         einer Schaltung
	 */
	protected List<LogicEditorElement> getElementsListOnEditor() {
		List<LogicEditorElement> eList = new ArrayList<LogicEditorElement>();
		for (LogicEditorElement e : this.elementsOnEditor.values()) {
			eList.add(e);
		}
		return eList;
	}

	/**
	 * Gibt alle <code>LogicEditorElement</code>-Objekte einer Schaltung
	 * zurueck, diese koennen aber nicht veraendert werden
	 * 
	 * @return <code>Map</code> - alle <code>LogicEditorElement</code>-Objekte
	 *         einer Schaltung
	 */
	public Map<String, LogicEditorElement> getUnmodifableElementsOnEditor() {
		return Collections.unmodifiableMap(this.elementsOnEditor);
	}

	/**
	 * Gibt die Anzahl der unverbundenen Eingaenge einer Schaltung zurueck.
	 * 
	 * @return <code>int</code> - die Anzahl der unverbundenen Eingaenge
	 */
	public int getNumberOfUnrelatedInputs() {
		return this.getUnrelatedInputs().size();
	}

	/**
	 * Gibt die Anzahl der unverbundenen Ausgaenge einer Schaltung zurueck.
	 * 
	 * @return <code>int</code> - die Anzahl der unverbundenen Ausgaenge
	 */
	public int getNumberOfUnrelatedOutputs() {
		return this.getUnrelatedOutputs().size();
	}

	/**
	 * Diese Methode liefert ein Liste von Zahlen, welche die Anzahl der
	 * Subnetze in der Schaltung und die jeweilige Anzahl der Elemente ermittelt
	 * 
	 * @return <code>List</code> - erster Integer Wert steht fuer die Anzahl der
	 *         Subnetze, alle folgenden fuer die jeweilige Anzahl der Elemente
	 *         eines Subnetzes
	 */
	public List<Integer> getNumberOfSubnets() {
		List<Integer> info = new ArrayList<Integer>();
		List<TreeMap<String, LogicEditorElement>> subNets = subNets();

		// falls Subnetze existieren, so wird die Liste generiert
		if (!subNets.isEmpty()) {
			info.add(subNets.size());
			for (TreeMap<String, LogicEditorElement> current : subNets) {
				info.add(current.size());
			}
			// ansonsten wird 0, 0 zurueck gegeben
		} else {
			info.add(0);
			info.add(0);
		}
		return info;
	}

	/**
	 * Ermittelt den Typ der Schaltung und gibt diesen zurueck. Wird
	 * <code>true</code> zurueck gegeben so handelt es sich um ein Schaltwerk,
	 * egal wie viele Element sich auf der Schaltung befinden. Wird aber
	 * <code>false</code> zurueck gegeben, so muss differenziert werden ob diese
	 * Information dadurch gewonnen wurde, weil der Algorithmus bis zum Ende
	 * ausgefuehrt wurde, oder ob die maximale Anzahl von Elementen
	 * ueberschritten wurde. Diese Performance Grenze dient dazu um den worst
	 * case zu verhinder und ist in der Klasse UtilityConstants als
	 * MAX_ELEM_CIRCUIT_INFO gespeichert.
	 * 
	 * @return <code>boolean</code> - <code>true</code>, wenn Schaltwerk;
	 *         <code>false</code> bei Schaltnetz
	 */
	public boolean isLogicCircuit() {

		// befindet sich in der Schaltung ein Clock-Element oder ein FlipFlop
		// so handelt es sich dabei um ein Schaltnetz
		for (LogicEditorElement e : this.getElementsListOnEditor()) {
			if (e.getType() == LogicType.CLOCK
					|| e.getType() == LogicType.FLIPFLOP) {
				return true;
			}
			if (e.getType() == LogicType.SELFDEFINED) {

				// handelt es sich um ein selbstdefiniertes Bauteil, so wird
				// einfach indirekt auf die gepeicherte Information des
				// zugehoerigen CircuitModels zugegriffen
				if (((LogicSelfdefinedEditorElement) e).getLogicCircuitType()) {
					return true;
				}
			}
		}

		// befindet sich kein Element in der Schaltung, welches dazu fuehrt,
		// dass es sich um ein Schaltnetz handelt wird letztendlich noch
		// auf Rueckkopplung geprueft, dies wird aber nur durchgefuehrt falls
		// die Anzahl der Elemente unter einer bestimmten Grenze liegt
		// (Performance)
		if (this.getElementsOnEditor().size() < UserSettings.maxElemCircuitInfo
				&& this.hasFeedback()) {
			return true;
		}

		// fallen alle Tests negativ aus, kann es sich nur noch um ein
		// Schaltnetz handeln oder die Anzahl der Element ist zu groß
		return false;
	}

	/**
	 * Diese Methode zeigt an, ob es sich bei diesem Bauteil um ein Schaltnetz
	 * oder Schaltwerk handelt. <b>Vorsicht!</b> Diese Information ist nur
	 * gueltig, wenn zuvor die <code>setLogicCircuitType()</code> aufgerufen
	 * wurde und danach keine Aenderung des <code>CircuitModel</code>
	 * stattgefunden hat. Diese Methode wird benoetigt, damit der Schaltungstyp
	 * von selbstdefinierten Bauteilen abgefragt werden kann.
	 * 
	 * @return <code>boolean</code> - gibt <code>true</code> zurueck wenn das
	 *         <code>CircuitModel</code> ein Schaltwerk ist, ansonsten
	 *         <code>false</code>
	 */
	protected boolean getLogicCircuitType() {
		return this.isLogicCircuit;
	}

	/**
	 * Gibt die Anzahl der Elemente fuer jeden Bauteiltyp zurueck.
	 * 
	 * @return <code>Map</code> - die Anzahl der Elemente fuer jeden Bauteiltyp
	 */
	public Map<LogicType, Integer> getNumberOfEachLogicType() {
		Map<LogicType, Integer> numberOfTypes = new HashMap<LogicType, Integer>();

		// Es wird eine Map ueber alle vorhandenen Logiktypen erzeugt und
		// initial mit 0 belegt
		for (LogicType type : LogicType.values()) {
			numberOfTypes.put(type, 0);
		}
		Integer number = 0;

		// Es werden alle Elemente des CircuitModels durchlaufen und dabei
		// an Hand des Logiktyps die jeweilige Anzahl erhoeht
		for (LogicEditorElement e : this.getElementsListOnEditor()) {
			number = numberOfTypes.get(e.getType()).intValue();
			numberOfTypes.remove(e.getType());
			numberOfTypes.put(e.getType(), (number + 1));
		}
		return numberOfTypes;
	}

	/**
	 * Erhoeht den Zaehler um 1.
	 */
	protected void increaseIdCounter() {
		this.counter++;
	}

	/**
	 * Gibt den aktuellen Wert des Zaehlers zurueck.
	 * 
	 * @return <code>int</code> - den ID-Zaehler
	 */
	protected int getIdCounter() {
		return this.counter;
	}

	/**
	 * Setzt den Namen des <code>CircuitModel</code>.
	 * 
	 * @param name
	 *            <code>String</code> - Name des <code>CircuitModel</code>
	 * @return <code>boolean</code> - gibt <code>true</code> zurueck wenn ein
	 *         String uebergeben wurde, ansonsten <code>false</code>
	 */
	protected boolean setName(String name) {
		if (name != null) {
			this.name = name;
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Gibt den Namen des <code>CircuitModel</code> zurueck.
	 * 
	 * @return <code>String</code> - den Namen des <code>CircuitModel</code>
	 */
	public String getName() {
		return name;
	}

	/**
	 * Diese Methode gibt alle <code>Input</code>-Objekte zurueck, die
	 * unverbunden sind
	 * 
	 * @return <code>List</code> - alle <code>Input</code>-Objekte, die
	 *         unverbunden sind
	 */
	protected List<Input> getUnrelatedInputs() {
		List<Input> unrelatedInputs = new ArrayList<Input>();

		// durchlaufen aller Elemente auf der Schaltung
		for (LogicEditorElement e : this.elementsOnEditor.values()) {

			// bei einem Schalter muss der Eingang nicht verbunden sein
			if (!LogicType.SETTERS.contains(e.getType())) {

				// jeder Eingang muss mit einem Ausgang verbunden sein
				for (Input i : e.getInputs().values()) {
					if (i.getSourceOutput() == null) {
						unrelatedInputs.add(i);
					}
				}
			}
		}
		return Collections.unmodifiableList(unrelatedInputs);
	}

	/**
	 * Diese Methode gibt alle <code>Output</code>-Objekte zurueck, die
	 * unverbunden sind.
	 * 
	 * @return <code>List</code> - alle <code>Output</code>-Objekte, die
	 *         unverbunden sind.
	 */
	protected List<Output> getUnrelatedOutputs() {
		List<Output> unrelatedOutputs = new ArrayList<Output>();

		// durchlaufen aller Elemente auf der Schaltung
		for (LogicEditorElement e : this.elementsOnEditor.values()) {

			// bei einer Lampe muss der Ausgang nicht verbunden sein
			if (!LogicType.GETTERS.contains(e.getType())) {

				for (Output o : e.getOutputs().values()) {

					// an jedem Ausgang muss mindestens eine Verbindung
					// vorhanden sein
					if (o.getTargetInputs().size() < 1) {
						unrelatedOutputs.add(o);
					}
				}
			}
		}
		return Collections.unmodifiableList(unrelatedOutputs);
	}

	/**
	 * Diese Methode gibt die DistanzMatrix der Schaltung zurueck
	 * 
	 * @return <code>ConnectionMatrix</code> - Distanzmatrix der Schaltung
	 */
	protected ConnectionMatrix getDistMatrix() {
		return this.doModifiedFloydWarshall(
				new ConnectionMatrix(this.getElementsListOnEditor()), false);
	}

	/**
	 * Prueft die Schaltung auf Rueckkopplung. Dabei wird nur die oberste Ebene
	 * betrachtet. d.h. es wird zum Beispiel nicht das Innere eines
	 * selbstdefinierten Bauteils betrachtet.
	 * 
	 * @return <code>boolean</code> - <code>true</code>, falls Rueckkopplung
	 *         vorhanden; <code>false</code> sonst
	 */
	public boolean hasFeedback() {
		ConnectionMatrix conM = new ConnectionMatrix(
				this.getElementsListOnEditor());

		// wird mit Hilfe der doModifiedFloydWarshall Methode eine Rueckkopplung
		// gefunden wird true zurueck gegeben
		if (doModifiedFloydWarshall(conM, true) == null) {
			return true;

			// wird keine Rueckkopplung gefunden wird false zurueck gegeben
		} else {
			return false;
		}
	}

	/**
	 * Diese Methode setzt den FloydWarshall Algorithmus um, dabei wird eine
	 * Matrix erzeugt in der man die kuerzesten Entfernungen zwischen den
	 * Elementen ablesen kann. Es ist auch moeglich primaer nur nach
	 * Ruckkopplungen, also Schleifen zu suchen, dazu muss der Parameter
	 * <code>hasFeedback</code> auf <code>true</code> gesetzt werden.
	 * Desweiteren werden auch die maximalen Distanzen in der Matrix gespeichert
	 * werden. Diese Distanzen ignorieren bewusst Zyklen, da ansonsten in vielen
	 * Faellen eine unendliche Distanz das Ergebnis waere.
	 * 
	 * @param conM
	 *            <code>ConnectionMatrix<\code> - Verbindungsmatrix
	 * @param hasFeedback
	 *            <code>boolean</code> - Parameter der bestimmt ob vorzeitig
	 *            abgebrochen werden soll, wenn eine Rueckkopplung gefunden
	 *            wurde
	 * @return <code>ConnectionMatrix</code> - ein ConnectionMatrix Objekt aus
	 *         dem sich die kuerzesten Entfernungen zwischen den einezelnen
	 *         Bauteilen ablesen laesst,
	 *         <code>null<\code> falls primaer nach einer Rueckkopplung gesucht 
	 *         und diese auch gefunden wurde
	 */
	private ConnectionMatrix doModifiedFloydWarshall(ConnectionMatrix conM,
			boolean hasFeedback) {
		int[][] matrix = conM.getMatrix();
		int[][] maxMatrix = new int[conM.getSize()][conM.getSize()];

		for (int i = 0; i < conM.getSize(); ++i) {
			for (int j = 0; j < conM.getSize(); ++j) {
				maxMatrix[i][j] = matrix[i][j];
			}
		}

		// modifizierter FloydWarshall Algorithmus
		boolean hasConnectionDistMat = true;
		boolean hasConnectionMaxDistMat = true;

		for (int k = 0; k < conM.getSize(); ++k) {
			for (int row = 0; row < conM.getSize(); ++row) {
				for (int col = 0; col < conM.getSize(); ++col) {

					// Dieser Bereich befasst sich mit der Distanzmatrix
					// ---------------------------------------------------
					// Pruefung ob der "Umweg" ueberhaupt existiert
					// dabei steht 0 fuer unendliche Distanz, also nicht
					// verbunden
					hasConnectionDistMat = true;
					if (matrix[row][k] == 0 || matrix[k][col] == 0) {
						hasConnectionDistMat = false;
					}

					// existiert eine Verbindung ueber den Umweg hasConnection =
					// true und ist einer der beiden Faelle gegeben: zuvor
					// existierte noch keine Verbindung matrix[][] = 0 oder
					// der neue Weg ist kuerzer, dann soll die Distanz des
					// neuen Weges gespeichert werden
					if (hasConnectionDistMat
							&& (matrix[row][col] == 0 || matrix[row][col] > (matrix[row][k] + matrix[k][col]))) {
						matrix[row][col] = matrix[row][k] + matrix[k][col];
					}
					// -------------------------------------------------------

					// Dieser Bereich befasst sich mit der maxDistanzmatrix
					// -------------------------------------------------------
					// Pruefung ob der Umweg ueberhaupt existiert
					// dabei steht 0 fuer unendliche Distanz, also nicht
					// verbunden
					hasConnectionMaxDistMat = true;
					if (maxMatrix[row][k] == 0 || maxMatrix[k][col] == 0) {
						hasConnectionMaxDistMat = false;
					}

					// existiert eine Verbindung ueber den Umweg hasConnection =
					// true und ist k ungleich row und k ungleich col (damit
					// wird sicher gestellt, damit keine Zyklen verwendet
					// werden) und ausserdem muss der neue Weg laenger sein, als
					// der bisherige
					if (hasConnectionMaxDistMat
							&& (k != row && k != col && maxMatrix[row][col] < (maxMatrix[row][k] + maxMatrix[k][col]))) {
						maxMatrix[row][col] = maxMatrix[row][k]
								+ maxMatrix[k][col];
					}
					// -------------------------------------------------------
				}
			}

			// falls hasFeedback true ist, wird geprueft ob ein Feedback
			// vorhanden ist
			if (hasFeedback) {

				// eine Schleife ist vorhanden, falls sich auf der Diagonalen
				// ein Wert ungleich 0 befindet
				for (int i = 0; i < matrix.length; ++i) {
					if (matrix[i][i] != 0) {
						return null;
					}
				}
			}
		}

		// sollte nicht zuvor abgebrochen worden sein, weil nur nach einer
		// Schleife gesucht wurde, werden die neuen Entfernungstabellen
		// gespeichert
		conM.setDistMatrix(matrix);
		conM.setMaxDistMatrix(maxMatrix);
		return conM;
	}

	/**
	 * Diese Methode testet ein <code>CircuitModel</code> auf die Eigenschaften
	 * eines selbstdefinierten Bauteils
	 * 
	 * @return <code>boolean</code> - <code>true</code> falls alle Tests
	 *         erfolgreich waren, ansonsten <code>false</code>
	 */
	public boolean isComponent() {
		// pruefen ob es sich um eine leere Schaltung handelt
		if (this.elementsOnEditor.isEmpty()) {
			return false;
		}

		// pruefen ob alle Eingaenge belegt sind
		if (this.getUnrelatedInputs().size() > 0) {
			return false;
		}

		// pruefen ob es eventuell Teilnetze ohne Element vom Typ SETTERS und
		// GETTERS gibt
		List<LogicEditorElement> sWcOL = incorrectSubnets();
		if (sWcOL.size() > 0) {
			return false;
		}

		// hat die Schaltung die vorhergehenden Tests bestanden, so darf dieses
		// als Bauteil verwendet werden
		return true;
	}

	/**
	 * Diese Methode prueft an Hand einer Liste von Netzen die in der Schaltung
	 * existieren, ob alle gefordeten Bauteile im jeweiligen Netz vorhanden sind
	 * 
	 * @return <code>List<LogicEditorElment></code> - Liste mit den Elementen,
	 *         die nicht mit allen geforderten Elementen (z.B. Lampe, Clock,
	 *         Schalter) in Verbindung stehen
	 */
	protected List<LogicEditorElement> incorrectSubnets() {

		// Anfordern aller vorhandenen Subnetze
		List<TreeMap<String, LogicEditorElement>> subNets = subNets();
		List<LogicEditorElement> unconnectedElements = new ArrayList<LogicEditorElement>();
		boolean hasSetter;
		boolean hasGetter;
		boolean hasLogicComponent;

		// durchlaufen aller Subnetze und pruefen, ob die geforderten Elemente
		// vorhanden sind
		for (TreeMap<String, LogicEditorElement> current : subNets) {
			hasSetter = false;
			hasGetter = false;
			hasLogicComponent = false;

			// Sollten bereits die geforderten Bauteile ermittelt worden sein,
			// so brauchen keine weiteren Elemente mehr des Sets ueberprueft
			// werden
			if (hasSetter && hasGetter && hasLogicComponent) {
				break;
			} else {
				for (LogicEditorElement e : current.values()) {

					// ist ein SETTER vorhanden so existiert ein Bauteil mit
					// welchem es moeglich ist einen logischen Wert anzulegen
					if (LogicType.SETTERS.contains(e.getType())) {
						hasSetter = true;
					} else {

						// ist eine Lampe vorhanden so existiert ein Bauteil mit
						// welchem logische Werte abgelesen werden koennen
						if (LogicType.GETTERS.contains(e.getType())) {
							hasGetter = true;

							// handelt es sich bei dem Element weder um einen
							// SetterTypen noch um einen GetterTypen so muss es
							// sich um einen weiteren Typen handeln
						} else {
							hasLogicComponent = true;
						}
					}

				}
			}

			// ist eines der geforderten Bauteile nicht vorhanden so werden die
			// Elemente des zugehoerigen Sets vermerkt und als Rueckgabparameter
			// zurueckgegeben
			if (!hasSetter || !hasGetter || !hasLogicComponent) {
				unconnectedElements.addAll(current.values());
			}
		}

		// zurueckgeben der Elemente die nicht mit einem Element verbunden sind,
		// welches die Moeglichkeit besitzt einen logischen Wert zu setzen
		return Collections.unmodifiableList(unconnectedElements);
	}

	/**
	 * Diese Methode bestimmt die Teilnetze einer Schaltung und gibt diese als
	 * Liste zurueck
	 * 
	 * @return <code>List<TreeMap<String, LogicEditorElement>></code> Liste mit
	 *         Baeumen, welche die zugehoerigen Elemente enthalten
	 */
	private List<TreeMap<String, LogicEditorElement>> subNets() {
		List<LogicEditorElement> elements = this.getElementsListOnEditor();
		List<TreeMap<String, LogicEditorElement>> subnets = new ArrayList<TreeMap<String, LogicEditorElement>>();
		List<TreeMap<String, LogicEditorElement>> elementNets = new ArrayList<TreeMap<String, LogicEditorElement>>();
		LogicEditorElement relatedE;
		boolean hasSubnet;
		boolean hasOwnSubnet;

		// von jedem Element muss sein eigenes Subnetz und die in Verbindung
		// stehenden Elemente ermittelt werden
		for (LogicEditorElement e : elements) {
			elementNets.clear();
			hasOwnSubnet = false;

			// bestimmen des evtl. bereits vorhandenen Subnetz des zu
			// untersuchenden Elements
			for (TreeMap<String, LogicEditorElement> net : subnets) {
				if (net.containsKey(e.getName())) {
					elementNets.add(net);
					hasOwnSubnet = true;
				}
			}

			// bestimmen der in Verbindung stehenden Elemente
			for (Input i : e.getInputs().values()) {
				if (i.getSourceOutput() != null) {

					// Zuerst wird in den bereits zugeordneten Subnets gesucht
					hasSubnet = false;
					relatedE = i.getSourceOutput().getMySuperElement();
					for (TreeMap<String, LogicEditorElement> net : elementNets) {
						if (net.containsKey(relatedE.getName())) {
							hasSubnet = true;
						}
					}

					// Anschliessend, falls noch kein Subnet gefunden wurde,
					// werden die bereits vorhandenen Subnetze durchsucht und
					// falls eines gefunden wurde, wird dieses dem Element
					// zugeordnet
					if (!hasSubnet) {
						for (TreeMap<String, LogicEditorElement> net : subnets) {
							if (net.containsKey(relatedE.getName())) {
								hasSubnet = true;
								elementNets.add(net);
							}
						}
					}

					// Wurde immer noch kein Subnet gefunden wird f�r dieses
					// Element ein Subnet erstellt
					if (!hasSubnet) {
						TreeMap<String, LogicEditorElement> tree = new TreeMap<String, LogicEditorElement>();
						tree.put(relatedE.getName(), relatedE);
						elementNets.add(tree);
					}
				}
			}

			// nun muss geschaut werden, ob das Element zugehoerige Subnetze
			// besitzt. Falls nicht wird ein eigenes dafuer angelegt
			if (elementNets.isEmpty()) {
				TreeMap<String, LogicEditorElement> tree = new TreeMap<String, LogicEditorElement>();
				tree.put(e.getName(), e);
				subnets.add(tree);

				// besitzt das Element Subnetze, so muss geklaert werden ob
				// es sich bereits in einem Subnetz befindet
			} else {

				// Zuvor werden alle vermerkten Subnetze vereinigt und aus der
				// subnet Liste entfernt
				TreeMap<String, LogicEditorElement> newTree = new TreeMap<String, LogicEditorElement>();
				for (TreeMap<String, LogicEditorElement> net : elementNets) {
					newTree.putAll(net);
					subnets.remove(net);
				}
				if (!hasOwnSubnet) {
					newTree.put(e.getName(), e);
				}

				// Abschließend wird der vereinigte bzw. neu erzeugt Tree wieder
				// zu den Subnetzen hinzugefuegt
				subnets.add(newTree);
			}
		}
		return subnets;
	}

	/**
	 * Diese Klasse erzeugt an Hand einer uebergebenen Liste aus
	 * <code>LogicEditorElement</code>'s eine Matrix, welche die Verbindungen
	 * der einzelnen Elemente speichert. Dabei gibt die Liste die Reihenfolge
	 * der Spalten und Zeilenbezeichner vor. Steht z.B. an erster Stelle eine
	 * Lampe, so sind dessen Vebindungen in der ersten Zeile aufgefuehrt.
	 * 
	 * @author Team Locsi
	 * 
	 */
	protected class ConnectionMatrix {
		private List<LogicEditorElement> elements;
		private int[][] matrix;
		private int size;
		private int[][] maxDistMatrix;
		boolean isDistMatrix = false;

		/**
		 * Konstruktor
		 * 
		 * @param elements
		 *            <code>List</code> - Elemente deren Verbindungen als Matrix
		 *            dargestellt werden sollen
		 */
		public ConnectionMatrix(List<LogicEditorElement> elements) {
			this.elements = elements;
			this.size = elements.size();
			this.generateConMatrix();
		}

		/**
		 * Diese Methode generiert eine Verbindungsmatrix
		 */
		private void generateConMatrix() {
			matrix = new int[size][size];

			// befuellen der Matrix, welche die Verbindungen zwischen den
			// Elementen repraesentiert z.B. AND verbunden mit OR
			// ___|And| OR|
			// And| 0 | 1 |
			// Or| 0 | 0 |
			int row = 0;
			int column = 0;
			for (LogicEditorElement e : elements) {
				for (Input i : e.getInputs().values()) {
					Output o = i.getSourceOutput();
					if (o != null) {
						column = elements.indexOf(o.getMySuperElement());
						if (column >= 0) {
							matrix[column][row] = 1;
						}
					}
				}
				++row;
			}
		}

		/**
		 * Diese Methode ermoeglicht es die primaere Matrix zu ueberschreiben
		 * und diese durch eine Distanzmatrix zu ersetzen
		 * 
		 * @param distM
		 *            <code>int[][]</code> neue Matrix
		 */
		protected void setDistMatrix(int[][] distM) {
			this.isDistMatrix = true;
			this.matrix = distM;
		}

		/**
		 * Setzt das Attribut <code>maxDistMatrix</code>
		 * 
		 * @param mDistM
		 *            <code>int[][]</code> - Der zusetztende Wert
		 */
		protected void setMaxDistMatrix(int[][] mDistM) {
			this.maxDistMatrix = mDistM;
		}

		/**
		 * Diese Methode gibt die akutelle Matrix zurueck
		 * 
		 * @return <code>int[][]</code>
		 */
		protected int[][] getMatrix() {
			return this.matrix;
		}

		/**
		 * Gibt <code>macDistMatrix</code> zurueck
		 * 
		 * @return <code>int[][]</code>
		 */
		protected int[][] getMaxMatrix() {
			return this.maxDistMatrix;
		}

		/**
		 * Diese Methode gibt die Elemente zuerucke, welche die Spalten- bzw.
		 * Zeilenbezeichner vorgeben
		 * 
		 * @return <code>List<LogicEditorElement></code> - Elemente der
		 *         Verbindungen
		 */
		protected List<LogicEditorElement> getElements() {
			return Collections.unmodifiableList(this.elements);
		}

		/**
		 * Diese Methode liefert die Groesse der Anzahl der Elemente und somi
		 * die Breite der quadratischen Matrix
		 * 
		 * @return <code>int</code> - Anzahl der Elemente
		 */
		protected int getSize() {
			return this.size;
		}
	}

	/**
	 * Diese Unterklasse ermoeglicht Schaltungsinformationen der Schaltung zu
	 * generieren und abzufragen. Sie wurde hauptsaechlich aus Effizienz-
	 * gruenden erstellt. Da die Methoden ihre Informationen gemeinsam aus einer
	 * Distanzmatrix generieren und diese somit nicht jedesmal neu erzeugt
	 * werden muss.
	 * 
	 * @author Team Locsi
	 * 
	 */
	public class CircuitInformation {
		CircuitModel cModel;
		ConnectionMatrix distMatrix;

		/**
		 * Konstruktor der Klasse. Dieser bekommt das zugehoerige
		 * <code>CircuitModel</code> uebergeben und es wird sofort eine
		 * Distanzmatrix erzeugt.
		 * 
		 * @param cModel
		 */
		public CircuitInformation(CircuitModel cModel) {
			this.cModel = cModel;

			if (cModel.getElementsListOnEditor().size() <= UserSettings.maxElemCircuitInfo) {
				this.distMatrix = doModifiedFloydWarshall(new ConnectionMatrix(
						cModel.getElementsListOnEditor()), false);
			}
		}

		/**
		 * Gibt die Anzahl der Verbindungen, die auf dem kuerzesten Weg von
		 * einem SETTER zu einem GETTER liegen, zurueck.
		 * 
		 * @return <code>int</code> - Anzahl der Verbindungen, die auf dem
		 *         kuerzesten Weg von einem SETTER zu einer GETTER liegen
		 */
		public int getMinDepth() {

			// falls distmatrix null ist (weil zu viele elemente)
			// wird -1 zurueckgegeben
			if (distMatrix == null) {
				return -1;
			}

			List<Integer> setters = new ArrayList<Integer>();
			List<Integer> getters = new ArrayList<Integer>();
			LogicEditorElement current;
			int min = this.distMatrix.size;
			boolean hasMin = false;

			// es werden die SETTERS und GETTERS bestimmt
			for (int i = 0; i < distMatrix.size; ++i) {
				current = this.distMatrix.getElements().get(i);
				if (LogicType.SETTERS.contains(current.getType())) {
					setters.add(i);
				}
				if (LogicType.GETTERS.contains(current.getType())) {
					getters.add(i);
				}
			}

			// bestimmen der kleinsten Distanz
			for (int sOc : setters) {
				for (int l : getters) {

					// pruefen ob ein Pfad vorhanden ist
					int dist = this.distMatrix.getMatrix()[sOc][l];
					if (dist > 0) {

						// bestimmen des kuerzesten Weges
						if (min > dist) {
							min = dist;
							hasMin = true;
						}
					}
				}
			}

			// sollte kein GETTER erreichbar sein wird 0 zurueckgegeben
			if (hasMin) {
				return min;
			} else {
				return 0;
			}
		}

		/**
		 * Gibt die Anzahl der Elemente, die auf dem laengsten Weg von einem
		 * Schalter zu einer Lampe liegen, zurueck.
		 * 
		 * @return <code>int</code> - Anzahl der Elemente, die auf dem laengsten
		 *         Weg von einem Schalter zu einer Lampe liegen
		 */
		public int getMaxDepth() {

			// falls distmatrix null ist (weil zu viele elemente)
			// wird -1 zurueckgegeben
			if (distMatrix == null) {
				return -1;
			}

			List<Integer> setters = new ArrayList<Integer>();
			List<Integer> getters = new ArrayList<Integer>();
			LogicEditorElement current;
			int max = 0;

			// es werden die Indizes der Schalter, Clocks und Lampen bestimmt
			for (int i = 0; i < distMatrix.size; ++i) {
				current = this.distMatrix.getElements().get(i);
				if (LogicType.SETTERS.contains(current.getType())) {
					setters.add(i);
				}
				if (LogicType.GETTERS.contains(current.getType())) {
					getters.add(i);
				}
			}

			// bestimmen der groessten Distanz
			for (int sOc : setters) {
				for (int l : getters) {

					// pruefen ob ein Pfad vorhanden ist
					int dist = this.distMatrix.getMaxMatrix()[sOc][l];
					if (dist != 0) {
						// bestimmen des laengsten Weges
						if (max < dist) {
							max = dist;
						}
					}
				}
			}
			return max;
		}

		/**
		 * Gibt die Anzahl aller Elemente der Schaltung zurueck.
		 * 
		 * @return <code>int</code> - die Anzahl aller Elemente der Schaltung
		 */
		public int getNumberOfElements() {
			return cModel.elementsOnEditor.size();
		}
	}
}